// !数字枚举

// 我们使用枚举值的元素值时，就像访问对象的属性一样，可以使用'.'操作符和'[]'两种形式访问里面的值
enum Status {
  Uploading,
  Success,
  Failed
}
console.log(Status.Uploading); // 0
console.log(Status["Success"]); // 1
console.log(Status.Failed); // 2

// 修改起始编号
enum Color {
  Red = 2,
  Blue,
  Yellow
}
console.log(Color.Red, Color.Blue, Color.Yellow); // 2 3 4

// 指定任意字段的索引值
enum Status2 {
  Success = 200,
  NotFound = 404,
  Error = 500
}
console.log(Status2.Success, Status2.NotFound, Status2.Error); // 200 404 500

// 指定部分字段，其他使用默认递增索引
enum Status {
  Ok = 200,
  Created,
  Accepted,
  BadRequest = 400,
  Unauthorized
}
console.log(Status.Created, Status.Accepted, Status.Unauthorized); // 201 202 401

// 数字枚举在定义值的时候，可以使用计算值和常量。
// 但如果某个字段使用了计算值或常量，那么该字段后面紧接着的字段必须设置初始值，这里不能使用默认的递增值了
const getValue = () => {
  return 0;
};
enum ErrorIndex {
  a = getValue(),
  // b, // error 枚举成员必须具有初始化表达式。
  // c // error 枚举成员必须具有初始化表达式。
}
enum RightIndex {
  a = getValue(),
  b = 1,
  c
}
const Start = 1;
enum Index {
  a = Start,
  // b, // error 枚举成员必须具有初始化表达式。
  // c // error 枚举成员必须具有初始化表达式。
}

// !反向映射
// 反向映射只支持数字枚举
enum Status3 {
  Success = 200,
  NotFound = 404,
  Error = 500
}
console.log(Status3["Success"]); // 200
console.log(Status3[200]); // 'Success'
console.log(Status3[Status["Success"]]); // 'Success'

// !字符串枚举
// 字符串枚举值要求每个字段的值都必须是字符串字面量，
// 或者是该枚举值中另一个字符串枚举成员
enum Message {
  Error = "Sorry, error",
  Success = "Hoho, success"
}
console.log(Message.Error); // 'Sorry, error'

enum Message2 {
  Error = "error message",
  ServerError = Error,
  ClientError = Error
}
console.log(Message2.Error); // 'error message'
console.log(Message2.ServerError); // 'error message'

// !异构枚举
// 异构枚举就是枚举值中成员值既有数字类型又有字符串类型(不建议使用)
enum Result {
  Faild = 0,
  Success = "Success"
}

// !枚举成员类型和联合枚举类型
// #(1)枚举类型成员类型
enum Animal {
  Dog = 1,
  Cat = 2
}
interface Dog {
  type: Animal.Dog; // 这里使用Animal.Dog作为类型，指定接口Dog的必须有一个type字段，且类型为Animal.Dog
}
interface Cat {
  type: Animal.Cat; // 这里同上
}
// let cat1: Cat = {
//   // type: Animal.Dog // error 不能将类型“Animal.Dog”分配给类型“Animal.Cat”。.Cat”
// };
let dog: Dog = {
  type: Animal.Dog
};

// #(2)联合枚举类型
enum Status3 {
  Off,
  On
}
interface Light {
  status: Status3;
}
enum Animal2 {
  Dog = 1,
  Cat = 2
}
// const light1: Light = {
//   // status: Animal.Dog // error 不能将类型“Animal.Dog”分配给类型“Status”。
// };
const light2: Light = {
  status: Status3.Off
};
const light3: Light = {
  status: Status3.On
};

// !运行时的枚举
enum E {
  A,
  B
}
const getIndex = (enumObj: { A: number }): number => {
  return enumObj.A;
};
console.log(getIndex(E)); // 0

// !const enum
// const enum(完全嵌入的枚举)，
// 在定义枚举的语句之前加上const关键字，这样编译后的代码不会创建这个对象，只是会从枚举里拿到相应的值进行替换
enum Status4 {
  Off,
  On
}
const enum Animal3 {
  Dog,
  Cat
}
const status2 = Status4.On;
const animal = Animal2.Dog;
